// Listing 16.6 demonstrates private inheritance
#include <iostream>
using namespace std;

/**Part**/
// Abstract base class of parts
class Part
{
    public:
        Part():itsPartNumber(1){}
        Part(int PartNumber):itsPartNumber(PartNumber){}
        virtual ~Part(){}
        int GetPartNumber() const
        {
            return itsPartNumber;
        }
        virtual void Display() const = 0;
    private:
        int itsPartNumber;
};

// implementation of pure virtual function so that
// derived classes can chain up
void Part::Display() const
{
    cout << "\nPart Number: " << itsPartNumber << endl;
}

/**Car Part**/
class CarPart : public Part
{
    public:
        CarPart():itsModelYear(94){}
        CarPart(int year, int partNumber);
        virtual void Display() const
        {
            Part::Display();
            cout << "Model Year: ";
            cout << itsModelYear << endl;
        }
    private:
        int itsModelYear;
};

CarPart::CarPart(int year, int partNumber):
    itsModelYear(year),Part(partNumber)
{}

/*AirPlane Part*/
class AirPlanePart : public Part
{
    public:
        AirPlanePart():itsEngineNumber(1){}
        AirPlanePart(int EngineNumber, int PartNumber);
        virtual void Display() const
        {
            Part::Display();
            cout << "Engine No.: ";
            cout << itsEngineNumber << endl;
        }
    private:
        int itsEngineNumber;
};

AirPlanePart::AirPlanePart(int EngineNumber, int PartNumber):
    itsEngineNumber(EngineNumber),
    Part(PartNumber)
{}

/**Part Node**/
class PartNode
{
    public:
        PartNode(Part*);
        ~PartNode();
        void SetNext(PartNode* node)
        {
            itsNext = node;
        }
        PartNode* GetNext() const;
        Part* GetPart() const;
    private:
        Part* itsPart;
        PartNode* itsNext;
};

// PartNode Implementations...
PartNode::PartNode(Part* pPart):
    itsPart(pPart),
    itsNext(0)
{}

PartNode::~PartNode()
{
    delete itsPart;
    itsPart = 0;
    delete itsNext;
    itsNext = 0;
}

// Returns NULL if no next PartNode

PartNode* PartNode::GetNext() const
{
    return itsNext;
}

Part* PartNode::GetPart() const
{
    if(itsPart)
    {
        return itsPart;
    }
    else
    {
        return NULL;   // error
    }
}

/**Part List**/ 
class PartsList
{
    public:
        PartsList();
        ~PartsList();
        // needs copy constructor and operator equals!
        void Iterate(void (Part::*f)()const) const;
        Part* Find(int& position, int PartNumber) const;
        Part* GetFirst() const;
        void Insert(Part*);
        Part* operator[] (int) const;
        int GetCount() const { return itsCount; }
        static PartsList& GetGlobalPartsList()
        {
            return GlobalPartsList;
        }
    private:
        PartNode* pHead;
        int itsCount;
        static PartsList GlobalPartsList;
};

PartsList::PartsList():
    pHead(0),
    itsCount(0)
{}

PartsList::~PartsList()
{
    delete pHead;
}

Part* PartsList::GetFirst() const
{
    if(pHead)
    {
        return pHead->GetPart();
    }
    else
    {
        return NULL;   // error catch here
    }
}

Part* PartsList::operator[](int offSet) const
{
    PartNode* pNode = pHead;
    if(!pHead)
    {
        return NULL;    // error catch here
    }

    if(offSet > itsCount)
    {
        return NULL;  // error
    }

    for(int i = 0; i < offSet; i++)
    {
        pNode = pNode->GetNext();
    }

    return pNode->GetPart();
}

Part* PartsList::Find(int & position, int PartNumber) const
{
    PartNode* pNode = 0;
    for(pNode = pHead, position = 0; pNode != NULL;
            pNode = pNode->GetNext(), position++)
    {
        if(pNode->GetPart()->GetPartNumber() == PartNumber)
        {
            break;
        }
    }

    if(pNode == NULL)
    {
        return NULL;
    }
    else
    {
        return pNode->GetPart();
    }
}

void PartsList::Iterate(void (Part::*func)()const) const
{
    if(!pHead)
    {
        return;
    }
    
    PartNode* pNode = pHead;
    do
    {
        (pNode->GetPart()->*func)();
    } while ((pNode = pNode->GetNext()) != 0);
}

void PartsList::Insert(Part* pPart)
{
    PartNode* pNode = new PartNode(pPart);
    PartNode* pCurrent = pHead;
    PartNode* pNext = 0;

    int New = pPart->GetPartNumber();
    int Next = 0;
    itsCount++;

    if(!pHead)
    {
        pHead = pNode;
        return;
    }

    // if this one is smaller than head
    // this one is the new head
    if(pHead->GetPart()->GetPartNumber() > New)
    {
        pNode->SetNext(pHead);
        pHead = pNode;
        return;
    }

    for(;;)
    {
        // if there is no next, append this new one
        if(!pCurrent->GetNext())
        {
            pCurrent->SetNext(pNode);
            return;
        }

        // if this goes after this one and before the next
        // then insert it here, otherwise get the next
        pNext = pCurrent->GetNext();
        Next = pNext->GetPart()->GetPartNumber();
        if(Next > New)
        {
            pCurrent->SetNext(pNode);
            pNode->SetNext(pNext);
            return;
        }
    }
}

class PartsCatalog : private PartsList
{
    public:
        void Insert(Part*);
        int Exists(int PartNumber);
        Part* Get(int PartNumber);
        operator+(const PartsCatalog &);
        void ShowAll() { Iterate(Part::Display); }
    private:
};

void PartsCatalog::Insert(Part* newPart)
{
    int partNumber = newPart->GetPartNumber();
    int offset;

    if(!Find(offset, partNumber))
    {
        PartsList::Insert(newPart);
    }
    else
    {
        cout << partNumber << " was the ";
        switch (offset)
        {
            case 0:
                cout << "first ";
                break;
            case 1:
                cout << "second ";
                break;
            case 2: 
                cout << "third ";
                break;
            default: 
                cout << offset + 1 << "th ";
        }
    }
}

int PartsCatalog::Exists(int PartNumber)
{
    int offset;
    Find(offset, PartNumber);
    return offset;
}

Part* PartsCatalog::Get(int PartNumber)
{
    int offset;
    return (Find(offset, PartNumber));
}

int main(void)
{
    PartsCatalog pc;
    Part* pPart = 0;
    int PartNumber;
    int value;
    int choice = 99;

    while(choice != 0)
    {
        cout << "(0)Quit (1)Car (2)Plane";
        cin >> choice;

        if(choice != 0)
        {
            cout << "New PartNumber: ";
            cin >> PartNumber;
            
            if(choice == 1)
            {
                cout << "Model Year?: ";
                cin >> value;
                pPart = new CarPart(value, PartNumber);
            }
            else
            {
                cout << "Engine Number: ";
                cin >> value;
                pPart = new AirPlanePart(value, PartNumber);
            }
            pc.Insert(pPart);
        }
    }
    pc.ShowAll();
    return 0;
}

